home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 3.2 / Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO / packet / n17jsrc / pcgen.asm < prev    next >
Assembly Source File  |  1991-02-04  |  18KB  |  821 lines

  1. ; Collection of assembler support routines for NOS
  2. ; Copyright 1991 Phil Karn, KA9Q
  3.  
  4.     .MODEL    MEMMOD,C
  5.     LOCALS
  6.     %MACS
  7.     .LALL
  8.     extrn    ctick:proc
  9.     public    eoi
  10.  
  11. ; Hardware vector for timer linkage
  12. ; We use the timer hardware channel here instead of the indirect BIOS
  13. ; channel (1ch) because the latter is sluggish when running under DoubleDos
  14. TIMEVEC    EQU    08h
  15.  
  16.     .DATA
  17.     public    Intstk,Stktop,Spsave,Sssave,Mtasker,Hashtab
  18.     extrn    Isat:word
  19. Spsave    dw    ?        ; Save location for SP during interrupts
  20. Sssave    dw    ?        ; Save location for SS during interrupts
  21. Intstk    dw    512 dup(?)    ; Interrupt working stack
  22. Stktop    equ    $        ; SP set here when entering interrupt
  23. Mtasker    db    ?        ; Type of higher multitasker, if any
  24. Hashtab    db    256 dup(?)    ; Modulus lookup table for iphash()
  25.     .CODE
  26. dbase    dw    @Data
  27. jtable    dw    l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15    
  28.  
  29. ; Re-arm 8259 interrupt controller(s)
  30. ; Should be called just after taking an interrupt, instead of just
  31. ; before returning. This is because the 8259 inputs are edge triggered, and
  32. ; new interrupts arriving during an interrupt service routine might be missed.
  33. eoi    proc
  34.     cmp    Isat,1
  35.     jnz    @@1        ; Only one 8259, so skip this stuff
  36.     mov    al,0bh        ; read in-service register from
  37.     out    0a0h,al        ; secondary 8259
  38.     nop            ; settling delay
  39.     nop
  40.     nop
  41.     in    al,0a0h        ; get it
  42.     or    al,al        ; Any bits set?
  43.     jz    @@1        ; nope, not a secondary interrupt
  44.     mov    al,20h        ; Get EOI instruction
  45.     out    0a0h,al        ; Secondary 8259 (PC/AT only)
  46. @@1:    mov    al,20h        ; 8259 end-of-interrupt command
  47.     out    20h,al        ; Primary 8259
  48.     ret
  49. eoi    endp
  50.  
  51. ; common routine for interrupt return
  52.     public    doret
  53.     label    doret    far
  54.     pop    es
  55.     pop    di
  56.     pop    si
  57.     pop    bp
  58.     pop    dx
  59.     pop    cx
  60.     pop    bx
  61.     pop    ax
  62.     mov    ss,Sssave
  63.     mov    sp,Spsave    ; restore original stack context
  64.     pop    ds
  65.     iret
  66.  
  67. ; istate - return current interrupt state
  68.     public    istate
  69. istate    proc
  70.     pushf
  71.     pop    ax
  72.     and    ax,200h
  73.     jnz    @@1
  74.     ret
  75. @@1:    mov    ax,1
  76.     ret
  77. istate    endp
  78.  
  79. ; dirps - disable interrupts and return previous state: 0 = disabled,
  80. ;    1 = enabled
  81.     public dirps
  82. dirps    proc
  83.     pushf            ; save flags on stack
  84.     pop    ax        ; flags -> ax
  85.     and    ax,200h        ; 1<<9 is IF bit
  86.     jz    @@1        ; ints are already off; return 0
  87.     mov    ax,1
  88.     cli            ; interrupts now off
  89. @@1:    ret
  90. dirps    endp
  91.  
  92. ; restore - restore interrupt state: 0 = off, nonzero = on
  93.     public    restore
  94. restore    proc
  95.     arg is:word
  96.     test    is,0ffffh
  97.     jz    @@1
  98.     sti
  99.     ret
  100. @@1:    cli    ; should already be off, but just in case...
  101.     ret
  102. restore    endp
  103.  
  104.  
  105. ; multitasker types
  106. NONE        equ    0
  107. DOUBLEDOS    equ    1
  108. DESQVIEW    equ    2
  109. WINDOWS3    equ    3
  110.  
  111. ; Relinquish processor so other task can run
  112.     public    giveup
  113. giveup    proc
  114.     pushf        ;save caller's interrupt state
  115.     sti        ;re-enable interrupts
  116.     cmp    mtasker, DOUBLEDOS
  117.     jnz    @@1
  118.     mov    al,2    ; 110 ms
  119.     mov    ah,0eeh
  120.     int    21h
  121.     popf        ; restore caller's interrupt state
  122.     ret
  123.  
  124. @@1:    cmp    mtasker, DESQVIEW
  125.     jnz    @@2
  126.     mov    ax, 1000h
  127.     int    15h
  128.     popf        ; restore interrupts
  129.     ret
  130.  
  131. @@2:    cmp    mtasker, WINDOWS3
  132.     jnz    @@3
  133.     mov    ax, 1680h
  134.     int    2fh
  135.     cmp    al, 80h    ; call supported?
  136.     jz    @@3    ; nope
  137.     popf        ; yes - restore interrupts
  138.     ret
  139.  
  140. @@3:    hlt        ; wait for an interrupt
  141.     popf        ; restore interrupts
  142.     ret
  143. giveup    endp
  144.  
  145. ; check for a multitasker running
  146.     public    chktasker
  147. chktasker    proc
  148.     mov    mtasker,NONE
  149.  
  150.     ; Check for Microsoft Windows
  151.     mov    ax,1600h
  152.     int    2fh
  153.     cmp    al, 00h        ; 0 means windows multitasking not running
  154.     jz    @@4
  155.     cmp    al, 80h        ; ditto for 80h return
  156.     jz    @@4
  157.     mov    mtasker, WINDOWS3
  158.     ret
  159.  
  160.     ; Check for DoubleDos
  161. @@4:    mov    ah,0e4h
  162.     int    21h
  163.     cmp    al,1
  164.     jz    @@1
  165.     cmp    al,2
  166.     jnz    @@2
  167. @@1:    mov    mtasker, DOUBLEDOS
  168.     ret
  169.  
  170.     ; Check for DESQVIEW
  171. @@2:    mov    ax, 2b01h
  172.     mov    cx, 4445h
  173.     mov    dx, 5351h
  174.     int    21h
  175.     cmp    al, 0ffh
  176.     jnz    @@3
  177.     ret
  178.  
  179. @@3:    mov    mtasker, DESQVIEW
  180.     ret
  181. chktasker    endp
  182.  
  183. ; getss - Read SS for debugging purposes
  184.     public    getss
  185. getss    proc
  186.     mov    ax,ss
  187.     ret
  188. getss    endp
  189.  
  190. ; clockbits - Read low order bits of timer 0 (the TOD clock)
  191. ; This works only for the 8254 chips used in ATs and 386s.
  192. ;
  193. ; The timer runs in mode 3 (square wave mode), counting down
  194. ; by twos, twice for each cycle. So it is necessary to read back the
  195. ; OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
  196. ; pin forms the most significant bit of the count. Unfortunately,
  197. ; the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
  198. ;
  199. ; The PC's clock design is soooo brain damaged...
  200.  
  201.     public    clockbits
  202. clockbits    proc
  203.     mov    al,0c2h    ; latch timer 0 count and status for reading
  204.     pushf
  205.     cli        ; make chip references atomic
  206.     out    43h,al    ; send latch command
  207.     in    al,40h    ; get status of timer 0
  208.     mov    bl,al    ; save status
  209.     in    al,40h    ; get lsb of count
  210.     mov    ah,al    ; save lsb
  211.     in    al,40h    ; get msb of count
  212.     popf        ; no more chip references
  213.     and    bl,80h    ; we're only interested in the OUT bit
  214.     xchg    ah,al    ; ax = count in correct order
  215.     shr    ax,1    ; count /= 2
  216.     jz    @@3    ; zero count requires carry propagation
  217. @@2:    or    ah,bl    ; combine with OUT bit as most sig bit of count
  218.     ret
  219. @@3:    xor    bl,80h    ; propagate carry by toggling OUT bit when cnt == 0
  220.     or    ah,bl    ; combine with !OUT bit as most sig bit of count
  221.     ret
  222.  
  223. clockbits    endp
  224.  
  225. ; Internet checksum subroutine
  226. ; Compute 1's-complement sum of data buffer
  227. ; Uses an unwound loop inspired by "Duff's Device" for performance
  228. ;
  229. ; Called from C as
  230. ; unsigned short
  231. ; lcsum(buf,cnt)
  232. ; unsigned short *buf;
  233. ; unsigned short cnt;
  234.     public    lcsum
  235. lcsum    proc
  236.     arg    buf:ptr,cnt:word
  237.  
  238.     if    @Datasize NE 0
  239.         uses    ds,si
  240.         lds    si,buf    ; ds:si = buf
  241.     else
  242.         uses    si
  243.         mov    si,buf    ; ds:si = buf (ds already set)
  244.     endif
  245.  
  246.     mov    cx,cnt        ; cx = cnt
  247.     cld            ; autoincrement si
  248.     mov    ax,cx
  249.     shr    cx,1        ; cx /= 16, number of loop iterations
  250.     shr    cx,1
  251.     shr    cx,1
  252.     shr    cx,1
  253.  
  254.     inc    cx        ; make fencepost adjustment for 1st pass
  255.     and    ax,15        ; ax = number of words modulo 16
  256.     shl    ax,1        ; *=2 for word table index
  257.     lea    bx,jtable    ; bx -> branch table
  258.     add    bx,ax        ; index into jump table
  259.     clc            ; initialize carry = 0
  260.     mov    dx,0        ; clear accumulated sum
  261.     jmp    word ptr cs:[bx]    ; jump into loop
  262.  
  263. ; Here the real work gets done. The numeric labels on the lodsw instructions
  264. ; are the targets for the indirect jump we just made.
  265. ;
  266. ; Each label corresponds to a possible remainder of (count / 16), while
  267. ; the number of times around the loop is determined by the quotient.
  268. ;
  269. ; The loop iteration count in cx has been incremented by one to adjust for
  270. ; the first pass.
  271. deloop:    lodsw
  272.     adc    dx,ax
  273. l15:    lodsw
  274.     adc    dx,ax
  275. l14:    lodsw
  276.     adc    dx,ax
  277. l13:    lodsw
  278.     adc    dx,ax
  279. l12:    lodsw
  280.     adc    dx,ax
  281. l11:    lodsw
  282.     adc    dx,ax
  283. l10:    lodsw
  284.     adc    dx,ax
  285. l9:    lodsw
  286.     adc    dx,ax
  287. l8:    lodsw
  288.     adc    dx,ax
  289. l7:    lodsw
  290.     adc    dx,ax
  291. l6:    lodsw
  292.     adc    dx,ax
  293. l5:    lodsw
  294.     adc    dx,ax
  295. l4:    lodsw
  296.     adc    dx,ax
  297. l3:    lodsw
  298.     adc    dx,ax
  299. l2:    lodsw
  300.     adc    dx,ax
  301. l1:    lodsw
  302.     adc    dx,ax
  303. l0:    loop    deloop        ; :-)
  304.  
  305.     adc    dx,0        ; get last carries
  306.     adc    dx,0
  307.     mov    ax,dx        ; result into ax
  308.     xchg    al,ah        ; byte swap result (8088 is little-endian)
  309.     ret
  310. lcsum    endp
  311.  
  312. ; Link timer handler into timer chain
  313. ; Arg == address of timer handler routine
  314. ; MUST be called exactly once before uchtimer is called!
  315.  
  316. toff    dw    ?        ; save location for old vector
  317. tseg    dw    ?        ;  must be in code segment
  318.  
  319.     public    chtimer
  320. chtimer    proc
  321.     arg    vec:far ptr
  322.     uses    ds
  323.  
  324.     mov    ah,35h        ; get current vector
  325.     mov    al,TIMEVEC
  326.     int    21h        ; puts vector in es:bx
  327.     mov    cs:tseg,es    ; stash
  328.     mov    cs:toff,bx
  329.  
  330.     mov    ah,25h
  331.     mov    al,TIMEVEC
  332.     lds    dx,vec        ; ds:si = vec
  333.  
  334.     int    21h        ; set new vector
  335.     ret
  336. chtimer    endp
  337.  
  338. ; unchain timer handler from timer chain
  339. ; MUST NOT be called before chtimer!
  340.     public    uchtimer
  341. uchtimer    proc
  342.     uses    ds
  343.  
  344.     mov    ah,25h
  345.     mov    al,TIMEVEC
  346.     mov    dx,toff
  347.     mov    ds,tseg
  348.     int    21h        ; restore old vector
  349.     ret
  350. uchtimer    endp
  351.  
  352. ; Clock tick interrupt handler. Note the use of "label" rather than "proc"
  353. ; here, necessitated by the fact that "proc" automatically generates BP-saving
  354. ; code that we don't want here.
  355.  
  356.     public    btick
  357.     label    btick    far
  358.  
  359.     pushf
  360.     push    ds
  361.     cli
  362.     mov    ds,cs:dbase    ; establish interrupt data segment
  363.  
  364.     mov    Sssave,ss    ; stash user stack context
  365.     mov    Spsave,sp
  366.  
  367.     mov    ss,cs:dbase
  368.     lea    sp,Stktop
  369.  
  370.     push    ax        ; save user regs on interrupt stack
  371.     push    bx
  372.     push    cx
  373.     push    dx
  374.     push    bp
  375.     push    si
  376.     push    di
  377.     push    es
  378.  
  379.     call    ctick
  380.  
  381.      pop    es
  382.     pop    di
  383.     pop    si
  384.     pop    bp
  385.     pop    dx
  386.     pop    cx
  387.     pop    bx
  388.     pop    ax
  389.     mov    ss,Sssave
  390.     mov    sp,Spsave    ; restore original stack context
  391.     pop    ds
  392.     popf
  393.     jmp    dword ptr [toff]        ; link to previous vector
  394.  
  395. ; Convert 32-bit int in network order to host order (dh, dl, ah, al)
  396. ; Called from C as
  397. ; int32 get32(char *cp);
  398.  
  399.     public    get32
  400. get32    proc
  401.     arg    cp:ptr
  402.     if    @Datasize NE 0
  403.         uses    ds,si
  404.         lds    si,cp    ; ds:si = cp
  405.     else
  406.         uses    si
  407.         mov    si,cp    ; ds:si = cp (ds already set)
  408.     endif
  409.  
  410.     cld
  411.     lodsw
  412.     mov    dh,al    ; high word to dx, a-swapping as we go
  413.     mov    dl,ah
  414.     lodsw
  415.     xchg    al,ah    ; low word stays in ax, just swap
  416.     ret
  417. get32    endp
  418.  
  419. ; Convert 16-bit int in network order to host order (ah, al)
  420. ; Called from C as
  421. ; int16 get16(char *cp);
  422.  
  423.     public    get16
  424. get16    proc
  425.     arg    cp:ptr
  426.     if    @Datasize NE 0
  427.         uses    ds,si
  428.         lds    si,cp    ; ds:si = cp
  429.     else
  430.         uses    si
  431.         mov    si,cp    ; ds:si = cp (ds already set)
  432.     endif
  433.  
  434.     lodsw        ; note: direction flag is don't-care
  435.     xchg    al,ah    ; word stays in ax, just swap
  436.     ret
  437. get16    endp
  438.  
  439. ; Convert 32-bit int to network order, returning new pointer
  440. ; Called from C as
  441. ; char *put32(char *cp,int32 x);
  442.  
  443.     public    put32
  444. put32    proc
  445.     arg    cp:ptr,x:dword
  446.     if    @Datasize NE 0
  447.         uses    ds,di
  448.         les    di,cp    ; es:di = cp
  449.         mov    ax,ss    ; our parameter is on the stack, and ds might not
  450.         mov    ds,ax    ;   be pointing to ss.
  451.     else
  452.         uses    di
  453.         mov    di,cp    ; es:di = cp
  454.         mov    ax,ds    ; point es at data segment
  455.         mov    es,ax
  456.     endif
  457.  
  458.     cld
  459.     mov    ax,word ptr (x+2)    ; read high word of machine version
  460.     xchg    ah,al            ; swap bytes
  461.     stosw                ; output in network order
  462.     mov    ax,word ptr x        ; read low word of machine version
  463.     xchg    ah,al            ; swap bytes
  464.     stosw                ; put in network order
  465.  
  466.     mov    ax,di    ; return incremented output pointer
  467.     if    @Datasize NE 0
  468.         mov    dx,es    ; upper half of pointer
  469.     endif
  470.     ret
  471. put32    endp
  472.  
  473. ; Convert 16-bit int to network order, returning new pointer
  474. ; Called from C as
  475. ; char *put16(char *cp,int16 x);
  476.  
  477.     public    put16
  478. put16    proc
  479.     arg    cp:ptr,x:word
  480.     uses    di
  481.     if    @Datasize NE 0
  482.         les    di,cp    ;es:di = cp
  483.     else
  484.         mov    di,cp    ; es:di = cp
  485.         mov    ax,ds
  486.         mov    es,ax
  487.     endif
  488.     cld
  489.     mov    ax,x    ; fetch source word in machine order
  490.     xchg    ah,al    ; swap bytes
  491.     stosw        ; save in network order
  492.     mov    ax,di    ; return new output pointer to user
  493.     if    @Datasize NE 0
  494.         mov    dx,es    ; upper half of pointer
  495.     endif
  496.     ret
  497. put16    endp
  498.  
  499. ; kbraw - raw, nonblocking read from console
  500. ; If character is ready, return it; if not, return -1
  501.     public    kbraw
  502. kbraw    proc
  503.     mov    ah,06h    ; Direct Console I/O
  504.     mov    dl,0ffh    ; Read from keyboard
  505.     int    21h    ; Call DOS
  506.     jz    @@1    ; zero flag set -> no character ready
  507.     mov    ah,0    ; valid char is 0-255
  508.     ret
  509. @@1:    mov    ax,-1    ; no char, return -1
  510.     ret
  511. kbraw    endp
  512.  
  513. if    @CPU AND 2
  514. ; fast I/O buffer routines
  515. ; version for 80186, 286, 386 (uses ins, outs instructions)
  516.  
  517. ; outbuf - put a buffer to an output port
  518.     public    outbuf
  519. outbuf    proc
  520.     arg    port:word,buf:ptr,cnt:word
  521.     if    @Datasize NE 0
  522.         uses    ds,si
  523.         lds    si,buf    ; ds:si = buf
  524.     else
  525.         uses    si
  526.         mov    si,buf    ;ds:si = buf (ds already set)
  527.     endif
  528.     mov    dx,port
  529.     mov    cx,cnt
  530.     cld
  531.     rep outsb        ; works only on PC/AT (80286)
  532.     mov    dx,ds
  533.     mov    ax,si        ; return pointer just past end of buffer
  534.     ret
  535. outbuf    endp
  536.  
  537. ; inbuf - get a buffer from an input port
  538.     public    inbuf
  539. inbuf    proc
  540.     arg    port:word,buf:ptr,cnt:word
  541.     uses    di
  542.     if    @Datasize NE 0
  543.         les    di,buf        ; es:di = buf
  544.     else
  545.         mov    di,buf        ; es:di = buf
  546.         mov    ax,ds
  547.         mov    es,ax
  548.     endif
  549.     mov    dx,port
  550.     mov    cx,cnt
  551.     cld
  552.     rep insb        ; works only on PC/AT (80286)
  553.     mov    dx,es
  554.     mov    ax,di        ; return pointer just past end of buffer
  555.     ret
  556. inbuf    endp
  557.  
  558. else
  559.  
  560. ; fast buffer I/O routines
  561. ; version for 8086/8
  562.  
  563. ; outbuf - put a buffer to an output port
  564.     public    outbuf
  565. outbuf    proc
  566.     arg    port:word,buf:ptr,cnt:word
  567.     if    @Datasize NE 0
  568.         uses    ds,si
  569.         lds    si,buf    ; ds:si = buf
  570.     else
  571.         uses    si
  572.         mov    si,buf    ; ds:si = buf (ds already set)
  573.     endif
  574.  
  575.     mov    dx,port
  576.     mov    cx,cnt
  577.     cld
  578.  
  579. ; If buffer doesn't begin on a word boundary, send the first byte
  580.     test    si,1    ; (buf & 1) ?
  581.     jz    @@even ; no
  582.     lodsb        ; al = *si++;
  583.     out    dx,al    ; out(dx,al);
  584.     dec    cx    ; cx--;
  585.     mov    cnt,cx    ; save for later test
  586. @@even:
  587.     shr    cx,1    ; cx = cnt >> 1; (convert to word count)
  588. ; Do the bulk of the buffer, a word at a time
  589.     jcxz    @@nobuf    ; if(cx != 0){
  590. @@deloop:
  591.     lodsw        ; do { ax = *si++; (si is word pointer)
  592.     out    dx,al    ; out(dx,lowbyte(ax));
  593.     mov    al,ah
  594.     out    dx,al    ; out(dx,hibyte(ax));
  595.     loop    @@deloop    ; } while(--cx != 0); }
  596. ; now check for odd trailing byte
  597. @@nobuf:
  598.     mov    cx,cnt
  599.     test    cx,1
  600.     jz    @@cnteven
  601.     lodsb        ; al = *si++;
  602.     out    dx,al
  603. @@cnteven:
  604.     mov    dx,ds
  605.     mov    ax,si        ; return pointer just past end of buffer
  606.     ret
  607. outbuf    endp
  608.  
  609. ; inbuf - get a buffer from an input port
  610.     public    inbuf
  611. inbuf    proc
  612.     arg port:word,buf:ptr,cnt:word
  613.     uses    di
  614.     if    @Datasize NE 0
  615.         les    di,buf    ; es:di = buf
  616.     else
  617.         mov    di,buf    ; es:di = buf
  618.         mov    ax,ds
  619.         mov    es,ax
  620.     endif
  621.     mov    dx,port
  622.     mov    cx,cnt
  623.     cld
  624.  
  625. ; If buffer doesn't begin on a word boundary, get the first byte
  626.     test    di,1    ; if(buf & 1){
  627.     jz    @@bufeven ;
  628.     in    al,dx    ; al = in(dx);
  629.     stosb        ; *di++ = al
  630.     dec    cx    ; cx--;
  631.     mov    cnt,cx    ; cnt = cx; } save for later test
  632. @@bufeven:
  633.     shr    cx,1    ; cx = cnt >> 1; (convert to word count)
  634. ; Do the bulk of the buffer, a word at a time
  635.     jcxz    @@nobuf    ; if(cx != 0){
  636. @@deloop:
  637.     in    al,dx    ; do { al = in(dx);
  638.     mov    ah,al
  639.     in    al,dx    ; ah = in(dx);
  640.     xchg    al,ah
  641.     stosw        ; *si++ = ax; (di is word pointer)
  642.     loop    @@deloop    ; } while(--cx != 0);
  643. ; now check for odd trailing byte
  644. @@nobuf:
  645.     mov    cx,cnt
  646.     test    cx,1
  647.     jz    @@cnteven
  648.     in    al,dx
  649.     stosb        ; *di++ = al
  650. @@cnteven:
  651.     mov    dx,es
  652.     mov    ax,di        ; return pointer just past end of buffer
  653.     ret
  654. inbuf    endp
  655.  
  656. endif
  657.  
  658.     public    longdiv
  659.  
  660. ; long unsigned integer division - divide an arbitrary length dividend by
  661. ; a 16-bit divisor. Replaces the dividend with the quotient and returns the
  662. ; remainder. Called from C as
  663. ;
  664. ; unsigned short
  665. ; longdiv(unsigned short divisor,int cnt,unsigned short *dividend);
  666. ;
  667. ;Register usage:
  668. ; di - divisor
  669. ; si - pointer into dividend array
  670. ; cx - loop counter, initialized to the number of 16-bit words in the dividend
  671. ; ax - low word of current dividend before each divide, current quotient after
  672. ; dx - remainder from previous divide carried over, becomes high word of
  673. ;      dividend for next divide
  674.  
  675. longdiv    proc
  676.     arg    divisor:word,cnt:word,dividend:ptr
  677.     if    @Datasize NE 0
  678.         uses    ds,si,di
  679.         lds    si,dividend
  680.     else
  681.         uses    si,di
  682.         mov    si,dividend    ;si -> dividend array
  683.     endif
  684.  
  685.     cmp    divisor,0        ; divisor == 0?
  686.     jne    @2            ; no, ok
  687.     xor    ax,ax            ; yes, avoid divide-by-zero trap
  688.     jmp    short @1
  689.  
  690. @2:    mov    dx,0            ; init remainder = 0
  691.     mov    cx,cnt            ; init cnt
  692.     mov    di,divisor        ; cache divisor in register
  693.  
  694. @@deloop:
  695.     mov    ax,word ptr [si]    ; fetch current word of dividend
  696.     cmp    ax,0            ; dividend == 0 ?
  697.     jne    @7            ; nope, must do division
  698.     cmp    dx,0            ; remainder also == 0?
  699.     je    @4            ; yes, skip division, continue
  700.  
  701. @7:    div    di            ; do division
  702.     mov    word ptr [si],ax    ; save quotient
  703.  
  704. @4:    inc    si            ; next word of dividend
  705.     inc    si
  706.     loop     @@deloop
  707.  
  708.     mov    ax,dx            ; return last remainder
  709. @1:    ret
  710.  
  711. longdiv    endp
  712.  
  713. ; long unsigned integer multiplication - multiply an arbitrary length
  714. ; multiplicand by a 16-bit multiplier, leaving the product in place of
  715. ; the multipler, returning the carry. Called from C as
  716. ;
  717. ; unsigned short
  718. ; longmul(unsigned short multiplier,int cnt,unsigned short *multiplier);
  719. ;
  720. ; Register usage:
  721. ; di = multiplier
  722. ; si = pointer to current word of multiplicand
  723. ; bx = carry from previous round
  724. ; cx = count of words in multiplicand
  725. ; dx,ax = scratch for multiply
  726.  
  727.     public longmul
  728. longmul    proc    far
  729.     arg    multiplier:word,n:word,multiplicand:ptr
  730.     if    @Datasize NE 0
  731.         uses    ds,si,di
  732.         lds    si,multiplicand
  733.     else
  734.         uses    si,di
  735.         mov    si,multiplicand    ; si -> multiplicand array
  736.     endif
  737.  
  738.     mov    di,multiplier        ; cache multiplier in register
  739.     xor    bx,bx            ; init carry = 0
  740.     mov    cx,n            ; fetch n
  741.     mov    ax,cx
  742.     shl    ax,1            ; *2 = word offset
  743.     add    si,ax            ; multiplicand += n
  744.  
  745. @@deloop:
  746.     dec    si
  747.     dec    si            ; work from right to left
  748.     mov    ax,word ptr [si]    ; fetch current multiplicand
  749.     or    ax,ax            ; skip multiply if zero
  750.     jz    @@nomult
  751.     mul    di            ; dx:ax <- ax * di
  752. @@nomult:
  753.     add    ax,bx            ; add carry from previous multiply
  754.     mov    word ptr [si],ax    ; save low order word of product
  755.     mov    bx,0            ; clear previous carry, leaving CF alone
  756.     adc    bx,dx            ; save new carry
  757.     xor    dx,dx            ; clear in case we skip the next mult
  758.     loop    @@deloop
  759.  
  760.     mov    ax,bx            ; return final carry
  761.     ret
  762. longmul    endp
  763.  
  764. ifdef    notdef
  765. ; divide 32 bits by 16 bits, returning both quotient and remainder
  766. ; This allows C programs that need both to avoid having to do two divisions
  767. ;
  768. ; Called from C as
  769. ;    long divrem(dividend,divisor)
  770. ;    long dividend;
  771. ;    short divisor;
  772. ;
  773. ;    The quotient is returned in the low 16 bits of the result,
  774. ;    and the remainder is returned in the high 16 bits.
  775.  
  776.     public    divrem
  777. divrem    proc
  778.     arg    dividend:dword,divisor:word
  779.     mov    ax,word ptr dividend
  780.     mov    dx,word ptr (dividend+2)
  781.     div    divisor
  782.     ret
  783. divrem    endp
  784. endif    
  785.  
  786. ; General purpose hash function for IP addresses
  787. ; Uses lookup table Hashtab[] initialized in ip.c
  788. ; Called from C as
  789. ; char hash_ip(int32 ipaddr);
  790.  
  791.     public hash_ip
  792. hash_ip    proc
  793.     arg    ipaddr:dword
  794.     lea    bx,Hashtab
  795.     mov    ax,word ptr ipaddr
  796.     xor    ax,word ptr (ipaddr+2)
  797.     xor    al,ah
  798.     xlat
  799.     xor    ah,ah
  800.     ret
  801. hash_ip    endp
  802.  
  803. ; Compute int(log2(x))
  804. ; Called from C as
  805. ; int log2(int16 x);
  806.  
  807.     public    log2
  808. log2    proc
  809.     arg    x:word
  810.     mov    cx,16
  811.     mov    ax,x
  812. @@2:    rcl    ax,1 
  813.     jc    @@1
  814.     loop    @@2
  815. @@1:    dec    cx
  816.     mov    ax,cx
  817.     ret
  818. log2    endp
  819.     end
  820.